using System;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using System.Windows.Forms;

using Pegasus.Diagnostics;
using Pegasus.Log4Net;
using Pegasus.Log4Net.Config;
using Pegasus.Log4Net.Core;
using Pegasus.Log4Net.Repository.Hierarchy;
using Pegasus.Windows.Forms;
using Pegasus.Windows.Forms.Log4Net;

namespace Pegasus.ServiceProcess
{
	/// <summary>
	/// This form is shown when the service is run as an application.
	/// </summary>
	internal partial class FormServiceApplication : FormEx
	{
		// Local Instance Values
		private ILog m_log = LogManager.GetLogger( typeof( FormServiceApplication ) );
		private ServiceExecutable m_service;
		private ServiceApplicationAppender m_appAppender;

		private AppendTextHandler m_textHandler;

		// Local Static Values
		private const string LOGGER_PERFIX = "Logger:";
		private const string LOGGER_ROOT = LOGGER_PERFIX + "Root";

		/// <summary>
		/// Initializes a new instance of the <see cref="T:FormServiceApplication"/> class.
		/// </summary>
		/// <param name="service">The reference to the service object.</param>
		public FormServiceApplication( ServiceExecutable service )
			: base( service.ServiceName + "DebugModeSettings" )
		{
			m_log.Debug( "FormServiceApplication()" );

			m_service = service;
			m_appAppender = new ServiceApplicationAppender( this );

			InitializeComponent();

			// Set the dialog title.
			Text = m_service.ServiceName;

			// Setup initial state of menu items.
			if( !m_service.CanPauseAndContinue )
			{
				MenuPause.Enabled = false;
				MenuPause.ToolTipText = "This service does not support Pause and Continue.  To enable this feature you must set the CanPauseAndContinue property to True.";
			}

			if( !m_service.CanShutdown )
			{
				MenuShutdown.Enabled = false;
				MenuShutdown.ToolTipText = "This service does not support system Shutdown.  To enable this feature you must set the CanShutdown property to True.";
			}


			// Setup the Log4Net appender to bind to our window
			SetupLogging();

			// Setup the text box delegate and the start up text.
			m_textHandler = new AppendTextHandler( TextTrace.AppendText );
			TextTrace.AppendText( string.Format( "{0} Started at {1}\n", m_service.ServiceName, DateTime.Now ) );
			TextTrace.AppendText( string.Format( "{0} Process Id {1}\n", m_service.ServiceName, Process.GetCurrentProcess().Id ) );
		}

		/// <summary>
		/// Handles the Click event of the MenuPause control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="args">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void MenuPause_Click( object sender, EventArgs args )
		{
			if( MenuPause.Text.StartsWith( "Pause" ) )
			{
				// Pause the service
				MenuPause.Text = "Resume Service";
				Text = m_service.ServiceName + " (Paused)";
				m_service.FireOnPause();
			}
			else
			{
				// Resume running the service
				MenuPause.Text = "Pause Service";
				Text = m_service.ServiceName;
				m_service.FireOnContinue();
			}
		}

		/// <summary>
		/// Handles the Click event of the MenuConfig control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="args">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void MenuConfig_Click( object sender, EventArgs args )
		{
			new FormConfigLog4Net( m_service.ServiceName, m_appAppender ).ShowDialog( this );
			SaveLogLevels();
		}

		/// <summary>
		/// Handles the Click event of the MenuShutdown control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="args">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void MenuShutdown_Click( object sender, EventArgs args )
		{
			m_service.FireOnShutdown();
			
			DialogResult = DialogResult.Cancel;
			Close();
		}

		/// <summary>
		/// Handles the Click event of the MenuExit control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="args">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
		private void MenuExit_Click( object sender, EventArgs args )
		{
			DialogResult = DialogResult.OK;
			Close();
		}

		/// <summary>
		/// Append the text to the end of the text box.
		/// </summary>
		/// <param name="text">The text.</param>
		internal void WriteText( string text )
		{
			
			// If the window has not been created yet or the window has
			// be destroyed already then just drop the text on the floor.
			if( !IsDisposed && IsHandleCreated )
			{
				// If we are on a background thread then invoke the append text 
				// method on the proper UI thread.
				if( InvokeRequired )
				{
					Invoke( m_textHandler, text );
				}
				else
				{
					TextTrace.AppendText( text );
					TextTrace.ScrollToCaret();
				}
			}
		}

		/// <summary>
		/// Setups the log4net.
		/// </summary>
		private void SetupLogging()
		{
			// Setup the .net trace and debug listeners.
			Trace.Listeners.Add( new ServiceApplicationListener( this ) );

			// Setup the log4net appenders
			if( LogManager.GetRepository().Configured )
			{
				ILogger[] loggers = LogManager.GetRepository().GetCurrentLoggers();
				foreach( ILogger iLogger in loggers )
				{
					Logger logger = iLogger as Logger;
					if( logger != null )
					{
                        try
                        {
                            logger.AddAppender( m_appAppender );
                        }
                        catch(NotSupportedException)
                        {
                            m_log.Warn( "Unable to add appender." );
                        }
					}
				}
			}
			else
			{
				BasicConfigurator.Configure( m_appAppender );
			}

			// Reload the log levels that the user set last time
			LoadLogLevels();
		}

		/// <summary>
		/// Loads the log levels.
		/// </summary>
		private void LoadLogLevels()
		{
			Hierarchy hierarchy = (Hierarchy) LogManager.GetRepository();

			foreach( string key in UserData.Keys )
			{
				if( key.StartsWith( LOGGER_PERFIX ) )
				{
					Logger logger;

					if( key == LOGGER_ROOT )
					{
						logger = hierarchy.Root;
					}
					else
					{
						string loggerName = key.Split( ':' )[ 1 ];
						logger = hierarchy.GetLogger( loggerName ) as Logger;
					}

					if( logger != null )
					{
						logger.Level = Level.Parse( (string) UserData[ key ] );
					}
				}
			}
		}

		/// <summary>
		/// Saves the log levels.
		/// </summary>
		private void SaveLogLevels()
		{
			Hierarchy hierarchy = (Hierarchy) LogManager.GetRepository();

			// Save the Root logger value
			Logger rootLogger = hierarchy.Root;
			UserData[ LOGGER_ROOT ] = rootLogger.Level.Name;

			// Save all other logger values
			ILogger[] loggers = hierarchy.GetCurrentLoggers();
			foreach( ILogger iLogger in loggers )
			{
				Logger logger = iLogger as Logger;
				if( logger != null )
				{
					string name = null;
					if( logger.Level != null )
					{
						name = logger.Level.Name;
					}

					UserData[ LOGGER_PERFIX + logger.Name ] = name;
				}
			}

			SaveUserData();
		}

		/// <summary>
		/// Trace listeners (and thus the Write/WriteLine method) my be called on any 
		/// thread, so we need to have an delegate to invoke on the UI thread to write
		/// the messages/text to the text box.
		/// </summary>
		/// <param name="text">The text to append</param>
		private delegate void AppendTextHandler( string text );
	}
}